package com.bc.bcquartzservice;

import com.bc.commoncore.config.ConfigBase;
import com.bc.commoncore.quartzutil.QuartzManager;
import com.bc.commoncore.util.HttpUtil;
import com.bc.commoncore.util.JsonUtil;
import com.bc.commoncore.util.StringUtils;
import com.bc.commoncore.util.SystemUtils;
import com.bc.commonentity.common.JsonResult;
import com.bc.commonentity.enums.HttpReturnRnums;
import com.bc.model.QuartzHttpModel;
import com.bc.model.QuartzServiceModel;
import com.bc.model.QuartzStartListReq;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.lang.management.ManagementFactory;
import java.util.*;
import java.util.stream.Collectors;

public class QuartzJobHelper {
    public static final String ParamMapKey = "QuartzServiceModel";
    private static List<QuartzServiceModel> oldListData = null;
    private static String oldVersionCode = "";
    private final static Logger log = LoggerFactory.getLogger(QuartzJobHelper.class);
    private static boolean isRefreshing = false;

    /**
     * 轮训获取已启动的服务列表
     *
     * @return
     * @author hailongzhao
     * @date 20160217
     */
//	public  static void init(Class cls,String appName) {
//		log.info("启动业务线程");
//		
//		Thread dThread = new Thread(new Runnable() {
//			@Override
//			public void run() {
//				String configPath = Config.getQuartzConfigUrl();
//				int appSource = Integer.parseInt(Config.getAppSource());
//				String configRate = Config.getConfigRate();
//				long sleepMillis = Integer.parseInt(configRate) * 1000;
//				log.info("每隔"+(sleepMillis/1000)+"秒发送"+configPath+"post请求");
//				while (true) {
//					try {
//						refreshDo(cls,appName,configPath, appSource);
//						Thread.sleep(sleepMillis);
//					} catch (Exception e) {
//						sendMail(appName,"每隔"+(sleepMillis/1000)+"秒更新quartz服务时异常:",e);
//					}
//				}
//			}
//		});
//		dThread.setDaemon(false);
//		dThread.start();
//	}
    public static void init(Class cls, String appName) {
        log.info("启动业务线程");
        String configRate = Config.getConfigRate();
        long sleepMillis = Integer.parseInt(configRate) * 1000;
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            public void run() {
                if (isRefreshing) {
                    return;
                }
                isRefreshing = true;
                String configPath = Config.getQuartzConfigUrl();
                int appSource = Integer.parseInt(Config.getPlatform_id());
                log.info("每隔" + configRate + "秒发送" + configPath + "post请求");
                try {
                    refreshDo(cls, appName, configPath, appSource);
                } catch (Exception e) {
                    sendMail(appName, "每隔" + configRate + "秒更新quartz服务时异常:", e);
                }
                isRefreshing = false;
            }
        }, 5000, sleepMillis);
    }

    public static void writePID() throws IOException {
        log.info("写进程id");
        File f = new File("device.pid");
        OutputStreamWriter writer = new OutputStreamWriter(
                new FileOutputStream(f));
        String processName = ManagementFactory.getRuntimeMXBean().getName();
        String pid = processName.substring(0, processName.indexOf("@"));
        writer.write(String.valueOf(pid));
        writer.flush();
        writer.close();
    }

    public static void sendMail(String appName, String remark, Exception e) {
        String msg = remark;
        if (e != null) {
            msg += String.format("%s\n%s\n", e.getMessage(), StringUtils.getStackTrace(e));
        }
        log.info(msg);
        String isSendMail = ConfigBase.getIsSendMail();
        if (isSendMail.equals("1")) {
            List<String> ipinfoList = SystemUtils.getLocalIpInfo();
            String appServerIP = JsonUtil.obj2string(ipinfoList);
            SystemUtils.sendAlertEmail(appName + "_java项目预警",
                    "appServerIP:" + appServerIP + "\n" + msg);
        }

    }

    /**
     * 获取已启动的服务列表，用于更新quartz中的服务
     *
     * @return
     * @author hailongzhao
     * @date 20160217
     */
    private static void refreshDo(Class cls, String appName, String configPath, int appSource) {
        try {
            QuartzHttpModel httpModel = getQuartzConfig(configPath, appSource);
            if (httpModel == null) {
                return;
            }
            log.info("VersionCode从" + oldVersionCode + "更新为" + httpModel.getVersionCode());
            oldVersionCode = httpModel.getVersionCode();
            List<QuartzServiceModel> newListData = httpModel.getListData();
            // 如果是第一次启动，就新增服务
            if (oldListData == null) {
                log.info("第一次启动，新增" + newListData.size() + "个服务");
                for (QuartzServiceModel quartzServiceModel : newListData) {
                    addJob(cls, quartzServiceModel);
                }
            } else {
                List<Integer> oldIds = oldListData.stream().map(t -> t.getId()).collect(Collectors.toList());
                List<Integer> newIds = newListData.stream().map(t -> t.getId()).collect(Collectors.toList());
                List<Integer> addNewIds = new ArrayList<Integer>(newIds);
                addNewIds.removeAll(oldIds);// addNewIds中剩余的是新增的服务
                log.info("本次需要新增" + addNewIds.size() + "个服务");
                for (Integer newId : addNewIds) {
                    QuartzServiceModel newModel = newListData.stream().filter(m -> m.getId() == newId).findFirst().get();
                    addJob(cls, newModel);
                }
                List<Integer> removeOldIds = new ArrayList<Integer>(oldIds);
                removeOldIds.removeAll(newIds);// removeOldIds中剩余的是需要停止的服务
                log.info("本次需要停止" + removeOldIds.size() + "个服务");
                for (Integer oldId : removeOldIds) {
                    QuartzServiceModel oldModel = oldListData.stream().filter(m -> m.getId() == oldId).findFirst().get();
                    removeJob(oldModel);
                }
                List<Integer> updateOldIds = new ArrayList<Integer>(oldIds);
                updateOldIds.retainAll(newIds);// updateOldIds中剩余的是交集，即可能需要重新设置的服务
                int resetNum = 0;
                for (Integer oldId : updateOldIds) {
                    QuartzServiceModel oldModel = oldListData.stream().filter(m -> m.getId().intValue() == oldId.intValue()).findFirst().get();
                    QuartzServiceModel newModel = newListData.stream().filter(m -> m.getId().intValue() == oldId.intValue()).findFirst().get();
                    // 请求地址或执行频率变更了，则需要重置服务
                    if (!oldModel.getRequrl().equals(newModel.getRequrl())
                            || !oldModel.getExec_time().equals(newModel.getExec_time())) {
                        resetNum++;
                        removeJob(oldModel);
                        addJob(cls, newModel);
                    }
                }
                log.info("本次需要重置" + resetNum + "个服务");
            }
            oldListData = newListData;
        } catch (Exception e) {
            sendMail(appName, "根据" + configPath + "获取配置更新quartz服务时异常:", e);
        }
    }

    private static void removeJob(QuartzServiceModel oldModel) throws Exception {
        //移除ChildJob类中的running标记，否则即使服务重新开启，服务无法运行
        ChildJob.clearOldRunningJobMapByKey(oldModel.getRequrl());
        QuartzManager.removeJob(getJobName(oldModel));
        //移除对应的重试任务
        removeRetryJob(oldModel);
    }

    private static void addJob(Class cls, QuartzServiceModel newModel) throws Exception {
        HashMap<String, Object> paramMap = new HashMap<String, Object>();
        paramMap.put(ParamMapKey, newModel);
        QuartzManager.addJob(getJobName(newModel), cls, newModel.getExec_time(), paramMap);
    }

    private static String getJobName(QuartzServiceModel newModel) {
        return "zhtxquartzjobname" + newModel.getId();
    }

    private static QuartzHttpModel getQuartzConfig(String configPath, int appSource) {
        // 发送post请求，获取最新的服务配置信息
        QuartzStartListReq req = new QuartzStartListReq();
        req.setAppSource(appSource);
        req.setVersionCode(oldVersionCode);
        String param = JsonUtil.obj2string(req);
        String result = HttpUtil.sendJsonPost(configPath, param);
        if (result == null || result.isEmpty()) {
            return null;
        }
        JsonResult httpModel = JsonUtil.str2obj(result, JsonResult.class);
        if (httpModel == null || httpModel.getCode() != HttpReturnRnums.Success.value()) {
            throw new RuntimeException(configPath + "返回值异常：" + result);
        }
        if (httpModel.getData() != null) {
            String js = JsonUtil.obj2string(httpModel.getData());
            QuartzHttpModel model = JsonUtil.str2obj(js, QuartzHttpModel.class);
            if (model.getListData() == null) {
                return null;
            }
            return model;
        }
        return null;
    }

    /**
     * 重试任务如果执行成功，则从重试队列中移除
     *
     * @param oldModel
     * @throws Exception
     */
    public static void removeRetryJob(QuartzServiceModel oldModel) {
        try {
            //移除ChildJob类中的running标记，否则即使服务重新开启，服务无法运行
            RetryChildJob.clearOldRunningJobMapByKey(oldModel.getRequrl());
            QuartzManager.removeJob(getRetryJobName(oldModel));
        } catch (Exception e) {
            log.error("移除重试任务时异常" + e.getMessage());
        }
    }

    /**
     * 如果某个定时任务的时间间隔超过5分钟,
     * 且本次执行失败，则加入重试队列，频率为每5分钟执行一次
     *
     * @param cls
     * @param newModel
     */
    public static void addRetryJob(Class cls, QuartzServiceModel newModel) {
        try {
            String jobName = getRetryJobName(newModel);
            if (QuartzManager.checkExists(jobName)) {
                return;
            }
            Date now = new Date();
            Date nextDate = QuartzManager.getNextValidTimeAfter(newModel.getExec_time(), now);
            long time = nextDate.getTime() - now.getTime();
            if (time > 5 * 60 * 1000) {
                HashMap<String, Object> paramMap = new HashMap<String, Object>();
                paramMap.put(ParamMapKey, newModel);
                QuartzManager.addJob(jobName, cls, "0 0/5 * * * ? ", paramMap);
            }
        } catch (Exception e) {
            log.error("添加重试任务异常" + e.getMessage());
        }
    }

    /**
     * 获取重试任务的名称
     *
     * @param newModel
     * @return
     */
    private static String getRetryJobName(QuartzServiceModel newModel) {
        return "zhtxquartzretryjobname" + newModel.getId();
    }

}
